﻿#region File and License Information
/*
<File>
	<License>
		Copyright © 2009 - 2017, Daniel Vaughan. All rights reserved.
		This file is part of Calcium (http://calciumsdk.net), 
		which is released under the MIT License.
		See file /Documentation/License.txt for details.
	</License>
	<CreationDate>2011-11-23 13:39:38Z</CreationDate>
</File>
*/
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace Outcoder.Reflection
{
	public static class EventInfoExtensions
	{
#if NETFX_CORE
		/* TODO: [DV] Comment. */
		public static Delegate CreateHandler(this EventInfo eventInfo, Action action)
		{
			ArgumentValidator.AssertNotNull(eventInfo, "eventInfo");
			ArgumentValidator.AssertNotNull(action, "action");

			/* Source: http://stackoverflow.com/questions/3478218/using-reflection-emit-to-implement-a-interface */
			Type handlerType = eventInfo.EventHandlerType;
			ParameterInfo[] eventParams = handlerType.GetTypeInfo().GetDeclaredMethod("Invoke").GetParameters();

			//lambda: (object x0, EventArgs x1) => d()
			IEnumerable<ParameterExpression> parameters
				= eventParams.Select(p => Expression.Parameter(p.ParameterType, "x"));
			// - assumes void method with no arguments but can be
			//   changed to accomodate any supplied method
			MethodCallExpression body = Expression.Call(
				Expression.Constant(action), action.GetType().GetTypeInfo().GetDeclaredMethod("Invoke"));
			LambdaExpression lambda = Expression.Lambda(body, parameters.ToArray());
			MethodInfo invokeMethodInfo = handlerType.GetTypeInfo().GetDeclaredMethod("Invoke");
			Delegate result = invokeMethodInfo.CreateDelegate(handlerType, lambda.Compile());

			return result;
		}
#else
				/* TODO: [DV] Comment. */
		public static Delegate CreateHandler(this EventInfo eventInfo, Action action)
		{
			ArgumentValidator.AssertNotNull(eventInfo, "eventInfo");
			ArgumentValidator.AssertNotNull(action, "action");

			/* Source: http://stackoverflow.com/questions/3478218/using-reflection-emit-to-implement-a-interface */
			Type handlerType = eventInfo.EventHandlerType;
			ParameterInfo[] eventParams = handlerType.GetMethod("Invoke").GetParameters();
			//lambda: (object x0, EventArgs x1) => d()
			IEnumerable<ParameterExpression> parameters
				= eventParams.Select(p => Expression.Parameter(p.ParameterType, "x"));
			// - assumes void method with no arguments but can be
			//   changed to accomodate any supplied method
			MethodCallExpression body = Expression.Call(
				Expression.Constant(action), action.GetType().GetMethod("Invoke"));
			LambdaExpression lambda = Expression.Lambda(body, parameters.ToArray());
			Delegate result = Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false);

			return result;
		}
#endif
	}
}
